package cn.org.tpeach.nosql.framework;


import cn.org.tpeach.nosql.annotation.Component;
import cn.org.tpeach.nosql.annotation.ComponentScan;
import cn.org.tpeach.nosql.annotation.JFrameMain;
import cn.org.tpeach.nosql.tools.*;
import javafx.stage.Stage;
import lombok.extern.slf4j.Slf4j;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.*;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Observer;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;

/**
 * @author tyz
 * @Title: JnFrameApplication
 * @ProjectName RedisLark
 * @Description:  
 * @date 2019-06-23 21:06
 * @since 1.0.0
 */
@Slf4j
public class LarkFrame {
	public final static LarkLog larkLog = new LarkLog();
	public static boolean builtInJre = false;
	public static  Stage primaryStage = null;
	public  static  ScheduledExecutorService executorService = Executors.newScheduledThreadPool(4);
	public static String REDIS_CONFIG_PATH = null;
	public static Properties APPLICATION_VALUE;
	static {
		try {
			APPLICATION_VALUE = PropertiesUtils.getProperties("application.properties");
		} catch (IOException e) {
			e.printStackTrace();
			System.exit(0);
		}
	}

	public static String getProjectEnv(){
		return   LarkFrame.APPLICATION_VALUE.getProperty("project.environment");
	}

	
	public static  void run(Class<? extends BaseApp> primarySource,String[] args) {
		try{
			String jreHome = System.getProperty("java.home");
			if(SystemUtil.isWindows() && StringUtils.isNotBlank(jreHome)){
				//判断使用的jre
				String path = LarkFrame.class.getProtectionDomain().getCodeSource().getLocation().getPath().toLowerCase().replaceAll("/","").replaceAll("\\\\","");
				jreHome = jreHome.toLowerCase().replaceAll("/","").replaceAll("\\\\","");
				jreHome = jreHome.substring(0,jreHome.length()-3);
				if(path.startsWith(jreHome)){
					log.info("使用内置jre");
					builtInJre = true;
				}
			}
		}catch (Exception e){}
		//日志文件解析
		loadindLarkLog();
		//注解扫描
		componentAnnotation(primarySource);
		//启动程序
		start(primarySource,args);
	}
	
	
	private static void start(Class<? extends BaseApp> primarySource, String[] args) {
		final JFrameMain main = (JFrameMain) AnnotationUtil.getClassAnnotation(primarySource,JFrameMain.class);
		try {
			BaseApp baseApp = primarySource.newInstance();
			REDIS_CONFIG_PATH = baseApp.getConfigPath();
			if(StringUtils.isBlank(REDIS_CONFIG_PATH)){
				log.error("REDIS_CONFIG_PATH can not be null");
				System.exit(0);
			}
//			System.out.println("REDIS_CONFIG_PATH："+REDIS_CONFIG_PATH);

			try {
				//读取配置文件
				ConfigParserUtils.getInstance().readConfigFile(REDIS_CONFIG_PATH);
			} catch (IOException e) {
				e.printStackTrace();
				System.exit(0);
			}
			Class<?> loadClass = Thread.currentThread().getContextClassLoader().loadClass(main.value());
			Constructor<?> constructor = loadClass.getConstructor(String[].class);   ;
			constructor.setAccessible(true);
			constructor.newInstance(new Object[] {args});
		} catch (ClassNotFoundException | NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
			log.error("启动类不正确",e);
			System.exit(0);
		}
		
	}


	private static void componentAnnotation(Class<?> primarySource) {
		// 获取需要扫描的包
		final ComponentScan annotation = (ComponentScan) AnnotationUtil.getClassAnnotation(primarySource,ComponentScan.class);
		final String[] packages = annotation.basePackages();
		if (packages != null && packages.length > 0) {
			for (String packageName : packages) {
				Set<String> classNameList = null;
				try {
					classNameList = AnnotationUtil.getClassName(packageName, true);
				} catch (IOException e1) {
					log.error(packageName+"注解获取失败",e1);
					System.exit(0);
				}

				if (DataStructureUtils.isNotEmpty(classNameList)) {
					Class<?> classes = null;
					Component componentAnnotation = null;
					ComponentBean beanInfo = null;
					for (String className : classNameList) {
						try {
							classes = Thread.currentThread().getContextClassLoader().loadClass(className);
							// 判断该注解类型是不是所需要的类型
							if (null != classes && null != (componentAnnotation = (Component) AnnotationUtil.getClassAnnotation(classes,Component.class))) {
								beanInfo = new ComponentBean();
								beanInfo.setBeanName(componentAnnotation.value());
								beanInfo.setScope(componentAnnotation.scope());
								beanInfo.setClassName(className);
								beanInfo.setLazy(componentAnnotation.lazy());
								BeanContext.setBean(beanInfo);
							}
						} catch (ClassNotFoundException e) {
							log.error(className+" Not Found",e);
							System.exit(0);
						}
					}

				}
			}
		}
		//自动注入
	}

	private static void loadindLarkLog() {
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		factory.setNamespaceAware(true); // never forget this!
		DocumentBuilder builder = null;
		try {
			builder = factory.newDocumentBuilder();
			InputStream resourceAsStream = LarkFrame.class.getClassLoader().getResourceAsStream("larkLog.xml");
			if(resourceAsStream != null){
				Document doc = builder.parse(resourceAsStream);
				XPathFactory pathFactory = XPathFactory.newInstance();
				XPath xpath = pathFactory.newXPath();
				XPathExpression pathExpression = xpath.compile("//appender");
				Object result = pathExpression.evaluate(doc, XPathConstants.NODESET);
				NodeList nodes = (NodeList) result;
				for (int i = 0; i < nodes.getLength(); i++) {
					Node item = nodes.item(i);
					NamedNodeMap attributes = item.getAttributes();
					Node node = attributes.getNamedItem("name");
					Class<?> clazz = null;
					try {
						clazz = Class.forName(node.getNodeValue());
						larkLog.addObserver((Observer) clazz.newInstance());
					} catch (ClassNotFoundException | IllegalAccessException|InstantiationException|ClassCastException e) {
						log.error("LarkLog addObserver "+node.getNodeValue()+" Fail",e);
						System.exit(0);
					}

				}
			}else {
				throw new FileNotFoundException("larkLog.xml文件不存在");
			}

		} catch (ParserConfigurationException | SAXException |IOException| XPathExpressionException e) {
			log.error("larkLog.xml parse fail");
		}
	}

}
